﻿namespace Hopfield.Simulator.WPF
{
    using Microsoft.Win32;
    using Microsoft.Windows.Controls;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        #region Fields
        
        private static object Mutex = new object();
        private int predictorN1 = 6;
        private int predictorN2 = 10;
        private int predictorDelta = 0;
        private DataTable dataTablePredictor = null;

        #endregion

        #region Constructor

        public MainWindow()
        {
            this.Hopfield = new Hopfield.Math.Hopfield();

            this.InitializeComponent();

            this.dataTablePredictor = new DataTable("Predictor");
            this.dataTablePredictor.Locale = new System.Globalization.CultureInfo("en-US");

            this.Loaded += (sender, e) =>
            {
                this.checkBoxColors.IsChecked = this.Hopfield.Colorized;

                // Set d
                int d = 128;
                this.Hopfield.Setd(d);
                this.textBoxD.Text = d.ToString(CultureInfo.InvariantCulture);
                this.textBoxPredictorD.Text = d.ToString(CultureInfo.InvariantCulture);

                // Set eta
                this.Hopfield.Seteta(0.7f);
                this.textBoxEta.Text = "0.7";
                this.textBoxPredictorEta.Text = "0.7";

                // Set MSE
                this.Hopfield.SetMSEmin(0.3f);

                // N1, N2
                this.textBoxPredictorN1.Text = this.predictorN1.ToString(CultureInfo.InvariantCulture);
                this.textBoxPredictorN2.Text = this.predictorN2.ToString(CultureInfo.InvariantCulture);

                // Delta
                this.textBoxPredictorDelta.Text = this.predictorDelta.ToString(CultureInfo.InvariantCulture);

                this.dataTablePredictor.TableCleared += (senderL, eL) =>
                {
                    this.RefreshTabPages();
                };
                this.dataTablePredictor.RowDeleted += (senderL, eL) =>
                {
                    this.RefreshTabPages();
                };
                this.dataTablePredictor.TableNewRow += (senderL, eL) =>
                {
                    this.RefreshTabPages();
                };

                this.ReCreatePredictorGrid(this.predictorN1);
            };
        }

        #endregion

        #region Properties

        public Hopfield.Math.Hopfield Hopfield { get; private set; }

        #endregion

        #region ThreadMetods

        void OpenHNFile()
        {
            bool picture = false;
            bool predictor = false;

            this.Dispatcher.Invoke((Action)(() =>
            {
                picture = (TabItem)this.mainTabControl.SelectedItem == this.tabPictures;
                predictor = (TabItem)this.mainTabControl.SelectedItem == this.tabPredictor;
            }
            ), null);

            if (true == picture)
            {
                string fileName = string.Empty;

                this.Dispatcher.Invoke((Action)(() =>
                {
                    this.statusBarStatusLabel.Text = "Opening...";

                    OpenFileDialog ofd = new OpenFileDialog();
                    {
                        ofd.Filter = "Hopfield Net (*.hn)|*.hn";
                        if (ofd.ShowDialog() != true)
                        {
                            return;
                        }

                        fileName = ofd.FileName;
                    }

                    if (true == string.IsNullOrEmpty(fileName))
                    {
                        this.statusBarStatusLabel.Text = "Ready.";
                        return;
                    }
                }
                ), null);

                this.Hopfield.OpenHNFile(fileName);

                this.Dispatcher.Invoke((Action)(() =>
                {
                    this.listBoxAttracotrs.Items.Clear();
                    int c = this.Hopfield.LCount;
                    for (int i = 0; i < c; i++)
                    {
                        this.listBoxAttracotrs.Items.Add(i.ToString(CultureInfo.InvariantCulture));
                    }

                    this.checkBoxColors.IsChecked = this.Hopfield.Colorized;
                    this.checkBoxColors.IsEnabled = this.listBoxAttracotrs.Items.Count == 0;

                    this.RefreshTabPages();

                    this.statusBarStatusLabel.Text = "Ready.";
                }
                ), null);
            }

            if (true == predictor)
            {
                this.LoadPredictor();
            }
        }

        void SaveHNFile()
        {
            bool picture = false;
            bool predictor = false;

            this.Dispatcher.Invoke((Action)(() =>
            {
                picture = (TabItem)this.mainTabControl.SelectedItem == this.tabPictures;
                predictor = (TabItem)this.mainTabControl.SelectedItem == this.tabPredictor;
            }
            ), null);

            if (true == picture)
            {
                string fileName = string.Empty;

                this.Dispatcher.Invoke((Action)(() =>
                {
                    statusBarStatusLabel.Text = "Saving...";

                    SaveFileDialog sfd = new SaveFileDialog();
                    {
                        sfd.Filter = "Hopfield Net (*.hn)|*.hn";
                        if (sfd.ShowDialog() != true) return;

                        fileName = sfd.FileName;
                    }

                    if (true == string.IsNullOrEmpty(fileName))
                    {
                        this.statusBarStatusLabel.Text = "Ready.";
                        return;
                    }
                }), null);

                this.Hopfield.SaveHNFile(fileName);

                this.Dispatcher.Invoke((Action)(() =>
                {
                    this.statusBarStatusLabel.Text = "Ready.";
                }), null);
            }

            if (true == predictor)
            {
                this.SavePredictor();
            }
        }

        void LearningToMSEmin(string kind)
        {
            ChartWindow chartWindowMSE = null;
            this.Dispatcher.Invoke((Action)(() =>
            {
                this.statusBarStatusLabel.Text = "Learning...";
                this.checkBoxCharts.IsEnabled = false;
            }
            ), null);
            float mse = 1.0f;
            float mseL = mse;
            float mseLL = mseL;
            this.Dispatcher.Invoke((Action)(() =>
            {
                if (true == this.checkBoxCharts.IsChecked)
                {
                    chartWindowMSE = new ChartWindow();
                    chartWindowMSE.ClearValues();
                    chartWindowMSE.SetValuesName("MSE");
                    chartWindowMSE.Left = this.Left + this.Width;
                    chartWindowMSE.Top = this.Top;
                    chartWindowMSE.Height = this.Height;
                    chartWindowMSE.Show();
                }
            }), null);
            do
            {
                mseLL = mseL;
                mseL = mse;
                mse = this.Hopfield.LearnStepHN(kind);
                this.Dispatcher.Invoke((Action)(() =>
                {
                    this.statusBarMSELabel.Text = string.Format(CultureInfo.InvariantCulture, "MSE = {0}", mse.ToString(CultureInfo.InvariantCulture));
                    if (true == checkBoxCharts.IsChecked)
                        chartWindowMSE.AddValue(mse);
                }
                ), null);
            }
            while (mse < mseL || mseL < mseLL || mse < mseLL);
            this.Dispatcher.Invoke((Action)(() =>
            {
                this.statusBarStatusLabel.Text = "Ready.";
                this.checkBoxCharts.IsEnabled = true;
            }
            ), null);
        }

        void SimulateToMSEmin(object kind)
        {
            string k = kind.ToString();
            ChartWindow chartWindowMSE = null;
            ChartWindow chartWindowE = null;
            Bitmap bmp = this.Hopfield.CreateBmp();
            this.Dispatcher.Invoke((Action)(() =>
            {
                this.statusBarStatusLabel.Text = "Simulating...";
                this.checkBoxCharts.IsEnabled = false;
                int np = 0;
                if (true == int.TryParse(textBoxNoisePercent.Text, NumberStyles.Integer, CultureInfo.InvariantCulture, out np))
                {
                    Hopfield.RandY(np);
                }
                Hopfield.DrawTextureUsingPointers(bmp);
                BitmapImage bi = new BitmapImage();
                bi.BeginInit();
                bi.StreamSource = ConvertImageToMemoryStream(bmp);
                bi.EndInit();
                imageSimulator.Source = bi;
            }
            ), null);
            float mse = 1.0f;
            float mseMin = mse;
            float mseMinL = mseMin;
            float energy = 0.0f;
            float energyLast = energy;
            float energyLastLast = energyLast;
            this.Dispatcher.Invoke((Action)(() =>
            {
                if (true == this.checkBoxCharts.IsChecked)
                {
                    chartWindowMSE = new ChartWindow();
                    chartWindowE = new ChartWindow();
                    chartWindowMSE.ClearValues();
                    chartWindowMSE.SetValuesName("MSE");
                    chartWindowMSE.Left = this.Left + this.Width;
                    chartWindowMSE.Top = this.Top;
                    chartWindowMSE.Show();
                    chartWindowE.ClearValues();
                    chartWindowE.SetValuesName("E");
                    chartWindowE.Left = chartWindowMSE.Left;
                    chartWindowE.Top = chartWindowMSE.Top + chartWindowMSE.Height;
                    chartWindowE.Show();
                }
            }
            ), null);
            Thread.Sleep(1000);
            do
            {
                mseMinL = mseMin;
                mseMin = mse;
                mse = this.Hopfield.CompHN(k);
                energyLastLast = energyLast;
                energyLast = energy;
                energy = this.Hopfield.Energy;
                this.Dispatcher.Invoke((Action)(() =>
                {
                    this.Hopfield.DrawTextureUsingPointers(bmp);
                    BitmapImage bi = new BitmapImage();
                    bi.BeginInit();
                    bi.StreamSource = ConvertImageToMemoryStream(bmp);
                    bi.EndInit();
                    this.imageSimulator.Source = bi;
                    this.statusBarMSELabel.Text = string.Format(CultureInfo.InvariantCulture, "MSE = {0}", mse.ToString(CultureInfo.InvariantCulture));
                    if (true == this.checkBoxCharts.IsChecked)
                    {
                        chartWindowMSE.AddValue(mse);
                        chartWindowE.AddValue(energy);
                    }
                }
                ), null);
            }
            while ((mse < mseMin || mseMin < mseMinL || mse < mseMinL) ||
                   (energy < energyLast || energyLast < energyLastLast || energy < energyLastLast));
            this.Dispatcher.Invoke((Action)(() =>
            {
                this.statusBarStatusLabel.Text = "Ready.";
                this.checkBoxCharts.IsEnabled = true;
            }
            ), null);
        }

        private void AddPredictorAttractors()
        {
            this.Dispatcher.Invoke((Action)(() =>
            {
                this.dataGridPredictor.IsEnabled = false;
            }
            ), null);
            try
            {
                if (this.Hopfield.NCount != this.predictorN1 * this.predictorN2)
                    this.Hopfield.Recreate(this.predictorN1 * this.predictorN2);

                for (int i = this.Hopfield.LCount - 1; i > 0; i--)
                    this.Hopfield.DelAttractor(i);

                this.Dispatcher.Invoke((Action)(() =>
                {
                    for (int i = 0; i < this.dataTablePredictor.Rows.Count; i++)
                    {
                        float[] y = new float[this.predictorN1 * this.predictorN2];
                        for (int j = 0; j < this.dataTablePredictor.Columns.Count; j++)
                        {
                            int v = (int)this.dataTablePredictor.Rows[i][j];
                            for (int k = 0; k < this.predictorN2; k++)
                            {
                                y[j * predictorN2 + k] = this.Hopfield.GetFValue(v);
                            }
                        }

                        this.Hopfield.AddPredictorAttractor(y);
                    }
                }
                ), null);
            }
            finally
            {
                this.Dispatcher.Invoke((Action)(() =>
                {
                    this.dataGridPredictor.IsEnabled = true;
                }
                ), null);
            }
        }

        private void LearningPredictor(object kind)
        {
            string k = kind.ToString();

            if (this.Hopfield.LCount != this.dataTablePredictor.Rows.Count ||
                this.Hopfield.NCount != this.predictorN1 * this.predictorN2)
            {
                this.AddPredictorAttractors();
                this.Hopfield.ClearW();
            }

            this.Dispatcher.Invoke((Action)(() =>
            {
                this.statusBarStatusLabel.Text = "Learning...";
            }
            ), null);

            float mse = 1.0f;
            float mseL = mse;
            float mseLL = mseL;
            do
            {
                mseLL = mseL;
                mseL = mse;
                mse = this.Hopfield.LearnStepHN(k);
                this.Dispatcher.Invoke((Action)(() =>
                {
                    this.statusBarMSELabel.Text = string.Format(CultureInfo.InvariantCulture, "MSE = {0}", mse.ToString(CultureInfo.InvariantCulture));
                }
                ), null);
            }
            while (mse < mseL || mseL < mseLL || mse < mseLL);

            this.Dispatcher.Invoke((Action)(() =>
            {
                statusBarStatusLabel.Text = "Ready.";
            }
            ), null);
        }

        private void SimulatePredictor(object kind)
        {
            string k = kind.ToString();

            if (this.Hopfield.LCount != this.dataTablePredictor.Rows.Count ||
                this.Hopfield.NCount != this.predictorN1 * this.predictorN2)
                this.LearningPredictor("p");

            this.Dispatcher.Invoke((Action)(() =>
            {
                this.statusBarStatusLabel.Text = "Simulating...";
            }), null);

            this.Hopfield.LoadKtoY(this.Hopfield.LCount - 1);

            float mse = 1.0f;
            float mseMin = mse;
            float mseMinL = mseMin;
            do
            {
                mseMinL = mseMin;
                mseMin = mse;
                mse = this.Hopfield.CompHN(k);
                // E = Hopfield.Energy;
                this.Dispatcher.Invoke((Action)(() =>
                {
                    this.statusBarMSELabel.Text = string.Format(CultureInfo.InvariantCulture, "MSE = {0}", CultureInfo.InvariantCulture, mse.ToString(CultureInfo.InvariantCulture));
                }
                ), null);

            }
            while (mse < mseMin || mseMin < mseMinL || mse < mseMinL);

            float[] y = this.Hopfield.Y;
            int[] row = new int[this.predictorN1];

            for (int i = 0; i < this.predictorN1; i++)
            {
                float r = 0.0f;
                for (int j = 0; j < this.predictorN2; j++)
                    r += y[i * this.predictorN2 + j] / ((float)this.predictorN2);
                row[i] = this.Hopfield.GetValueF(r) + this.predictorDelta;
            }

            this.Dispatcher.Invoke((Action)(() =>
            {
                this.dataTablePredictor.Rows.Add();
                DataRow dr = this.dataTablePredictor.Rows[this.dataTablePredictor.Rows.Count - 1];

                for (int i = 0; i < this.predictorN1; i++)
                {
                    dr[i] = row[i];
                }

                this.statusBarStatusLabel.Text = "Ready.";
            }
            ), null);
        }

        void ThreadOpenHNFile()
        {
            lock (Mutex)
            {
                this.OpenHNFile();
            }
        }

        void ThreadSaveHNFile()
        {
            lock (Mutex)
            {
                this.SaveHNFile();
            }
        }

        void ThreadLearnMethod(object kind)
        {
            string k = kind.ToString();
            lock (Mutex)
            {
                this.LearningToMSEmin(k);
            }
        }

        void ThreadSimulateMethod(object kind)
        {
            string k = kind.ToString();
            lock (Mutex)
            {
                this.SimulateToMSEmin(k);
            }
        }

        void ThreadLearnPredictorMethod(object kind)
        {
            string k = kind.ToString();
            lock (Mutex)
            {
                this.LearningPredictor(k);
            }
        }

        void ThreadSimulatePredictorMethod(object kind)
        {
            string k = kind.ToString();
            lock (Mutex)
            {
                this.SimulatePredictor(k);
            }
        }

        #endregion

        #region Menu Click events

        private void MenuItemFileOpen_Click(object sender, RoutedEventArgs e)
        {
            Thread th = new Thread(new ThreadStart(this.ThreadOpenHNFile));
            th.Start();
        }

        private void MenuItemFileSave_Click(object sender, RoutedEventArgs e)
        {
            Thread th = new Thread(new ThreadStart(this.ThreadSaveHNFile));
            th.Start();
        }

        private void MenuItemSimulatorLearn_Click(object sender, RoutedEventArgs e)
        {
            if ((TabItem)this.mainTabControl.SelectedItem == this.tabPictures)
            {
                ParameterizedThreadStart threadStart = new ParameterizedThreadStart(this.ThreadLearnMethod);
                Thread th = new Thread(threadStart);
                // 1st kind of learning - h - one step
                // 2nd kind of learning - d - recursive, the best
                // 3rd kind of learning - z - recursive, worst then above
                th.Start("d");
            }

            if ((TabItem)mainTabControl.SelectedItem == tabPredictor)
            {
                ParameterizedThreadStart threadStart = new ParameterizedThreadStart(this.ThreadLearnPredictorMethod);
                Thread th = new Thread(threadStart);
                // 3rd kind of learning - p - recursive for predictor
                th.Start("p");
            }
        }

        private void MenuItemSimulatorSimulate_Click(object sender, RoutedEventArgs e)
        {
            if ((TabItem)this.mainTabControl.SelectedItem == this.tabPictures)
            {
                ParameterizedThreadStart threadStart = new ParameterizedThreadStart(this.ThreadSimulateMethod);
                Thread th = new Thread(threadStart);
                // 1st kind - a - synchronus - fast
                // 2nd kind - s - asynchronus - slower
                // 3rd kind - r - randomly - slowest
                th.Start("a");
            }

            if ((TabItem)mainTabControl.SelectedItem == tabPredictor)
            {
                ParameterizedThreadStart threadStart = new ParameterizedThreadStart(this.ThreadSimulatePredictorMethod);
                Thread th = new Thread(threadStart);
                // 1st kind - a - synchronus - fast
                // 2nd kind - s - asynchronus - slower
                // 3rd kind - r - randomly - slowest
                th.Start("s");
            }
        }

        private void MenuItemAttractorsAdd_Click(object sender, RoutedEventArgs e)
        {
            lock (Mutex)
            {
                if ((TabItem)this.mainTabControl.SelectedItem == this.tabPictures)
                {
                    OpenFileDialog ofd = new OpenFileDialog();
                    {
                        ofd.Filter = "PPM ASCII Image (*.ppm)|*.ppm";
                        ofd.Multiselect = true;
                        if (ofd.ShowDialog() != true) return;

                        for (int i = 0; i < ofd.FileNames.Length; i++)
                        {
                            string fileName = ofd.FileNames[i];
                            this.Hopfield.Colorized = this.Hopfield.Colorized;
                            this.Hopfield.OpenImgFile(fileName);
                            this.Hopfield.AddAttractor();
                            this.listBoxAttracotrs.Items.Add(this.listBoxAttracotrs.Items.Count.ToString(CultureInfo.InvariantCulture));
                        }

                        this.checkBoxColors.IsEnabled = this.listBoxAttracotrs.Items.Count == 0;

                        this.RefreshTabPages();
                    }
                }

                if ((TabItem)this.mainTabControl.SelectedItem == this.tabPredictor)
                {
                    this.dataTablePredictor.Rows.Add();

                    this.RefreshTabPages();
                }
            }
        }

        private void MenuItemAttractorsRemove_Click(object sender, RoutedEventArgs e)
        {
            lock (Mutex)
            {
                if ((TabItem)this.mainTabControl.SelectedItem == this.tabPictures)
                {
                    int k = this.listBoxAttracotrs.SelectedIndex;
                    if (k == -1) return;

                    this.Hopfield.DelAttractor(k);

                    int c = this.Hopfield.LCount;
                    this.listBoxAttracotrs.Items.Clear();
                    for (int i = 0; i < c; i++)
                    {
                        this.listBoxAttracotrs.Items.Add(i);
                    }

                    this.checkBoxColors.IsEnabled = this.listBoxAttracotrs.Items.Count == 0;

                    this.RefreshTabPages();
                }

                if ((TabItem)this.mainTabControl.SelectedItem == this.tabPredictor)
                {
                    this.dataTablePredictor.Rows.RemoveAt(this.dataGridPredictor.SelectedIndex);

                    this.RefreshTabPages();
                }
            }
        }

        #endregion

        #region Helper methods and Window events

        void RefreshTabPages()
        {
            this.Dispatcher.Invoke((Action)(() =>
            {
                this.tabPredictor.Visibility = this.listBoxAttracotrs.Items.Count > 0 ? Visibility.Hidden : Visibility.Visible;
                this.tabPictures.Visibility = this.dataTablePredictor.Rows.Count > 0 ? Visibility.Hidden : Visibility.Visible;
            }
            ), null);
        }

        private MemoryStream ConvertImageToMemoryStream(System.Drawing.Image img)
        {
            var ms = new MemoryStream();
            img.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
            return ms;
        }

        private void ListBoxAttracotrs_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (this.listBoxAttracotrs.SelectedItem == null) return;

            int i = int.Parse(this.listBoxAttracotrs.SelectedItem.ToString(), CultureInfo.InvariantCulture);
            this.Hopfield.LoadKtoY(i);
            Bitmap bmp = this.Hopfield.CreateBmp();
            this.Hopfield.DrawTextureUsingPointers(bmp);
            BitmapImage bi = new BitmapImage();
            bi.BeginInit();
            bi.StreamSource = this.ConvertImageToMemoryStream(bmp);
            bi.EndInit();
            this.imageSimulator.Source = bi;
        }

        private void TextBoxNoisePercent_TextChanged(object sender, TextChangedEventArgs e)
        {
        }

        private void TextBoxD_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (false == this.IsInitialized) return;

            int d = 0;
            if ((TabItem)this.mainTabControl.SelectedItem == this.tabPictures)
            {
                if (true == int.TryParse(textBoxD.Text, NumberStyles.Integer, CultureInfo.InvariantCulture, out d))
                {
                    textBoxPredictorD.Text = d.ToString(CultureInfo.InvariantCulture);
                }
            }

            if ((TabItem)this.mainTabControl.SelectedItem == this.tabPredictor)
            {
                if (true == int.TryParse(textBoxPredictorD.Text, NumberStyles.Integer, CultureInfo.InvariantCulture, out d))
                {
                    this.textBoxD.Text = d.ToString(CultureInfo.InvariantCulture);
                }
            }

            this.Hopfield.Setd(d);
        }

        private void TextBoxEta_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (false == this.IsInitialized) return;

            float eta = 0.7f;
            if ((TabItem)this.mainTabControl.SelectedItem == this.tabPictures)
            {
                if (true == float.TryParse(textBoxEta.Text, NumberStyles.Float, CultureInfo.InvariantCulture, out eta))
                {
                    textBoxPredictorEta.Text = eta.ToString(CultureInfo.InvariantCulture);
                }
            }
            if ((TabItem)this.mainTabControl.SelectedItem == this.tabPredictor)
            {
                if (true == float.TryParse(textBoxPredictorEta.Text, NumberStyles.Float, CultureInfo.InvariantCulture, out eta))
                {
                    textBoxEta.Text = eta.ToString(CultureInfo.InvariantCulture);
                }
            }
            this.Hopfield.Seteta(eta);
        }

        private void CheckBoxCharts_Checked(object sender, RoutedEventArgs e)
        {
        }

        private void CheckBoxColors_Checked(object sender, RoutedEventArgs e)
        {
            if (false == this.IsInitialized) return;

            this.Hopfield.Colorized = this.checkBoxColors.IsChecked.Value;
        }

        private void TextBoxPredictorDelta_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (false == this.IsInitialized) return;

            int delta = 0;
            if (true == int.TryParse(textBoxPredictorDelta.Text, NumberStyles.Integer, CultureInfo.InvariantCulture, out delta))
            {
                this.predictorDelta = delta;
            }
        }

        private void TextBoxPredictroN_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (false == this.IsInitialized) return;

            int n1 = 0;
            if (true == int.TryParse(textBoxPredictorN1.Text, NumberStyles.Integer, CultureInfo.InvariantCulture, out n1))
            {
                this.predictorN1 = n1;
            }

            int n2 = 0;
            if (true == int.TryParse(textBoxPredictorN2.Text, NumberStyles.Integer, CultureInfo.InvariantCulture, out n2))
            {
                this.predictorN2 = n2;
            }

            this.Hopfield.Recreate(this.predictorN1 * this.predictorN2);
            this.ReCreatePredictorGrid(this.predictorN1);
        }

        void ReCreatePredictorGrid(int columnsNumber)
        {
            if (false == this.IsInitialized) return;

            this.dataGridPredictor.ItemsSource = null;

            this.dataTablePredictor.BeginInit();
            this.dataGridPredictor.BeginInit();

            this.dataTablePredictor.Clear();
            this.dataTablePredictor.Columns.Clear();

            this.dataGridPredictor.Columns.Clear();

            for (int i = 0; i < columnsNumber; i++)
            {
                DataColumn dc = new DataColumn()
                {
                    AllowDBNull = false,
                    ColumnName = "V" + i,
                    DataType = typeof(int),
                    DefaultValue = 0
                };
                this.dataTablePredictor.Columns.Add(dc);
            }

            this.dataGridPredictor.EndInit();
            this.dataTablePredictor.EndInit();

            this.dataGridPredictor.ItemsSource = this.dataTablePredictor.DefaultView;

            this.dataTablePredictor.Rows.Add();
            this.dataTablePredictor.Rows.RemoveAt(0);
        }

        #endregion

        #region Grid Save and Load

        void SavePredictor()
        {
            lock (Mutex)
            {
                this.Dispatcher.Invoke((Action)(() =>
                {
                    statusBarStatusLabel.Text = "Saving...";
                }
                ), null);

                string fileName = string.Empty;

                this.Dispatcher.Invoke((Action)(() =>
                {
                    SaveFileDialog sfd = new SaveFileDialog();
                    {
                        sfd.Filter = "Hopfiled Predictor File (*.hpf)|*.hpf";
                        if (sfd.ShowDialog() != true) return;

                        fileName = sfd.FileName;
                    }
                }
                ), null);

                if (true == string.IsNullOrEmpty(fileName)) return;

                DataSet ds = new DataSet("Predictor");
                ds.Locale = new CultureInfo("en-US");

                try
                {
                    ds.Tables.Add(this.dataTablePredictor);
                    ds.WriteXml(fileName);
                }
                finally
                {
                    ds.Tables.Remove(this.dataTablePredictor);
                }

                this.Dispatcher.Invoke((Action)(() =>
                {
                    this.statusBarStatusLabel.Text = "Ready.";
                }
                ), null);
            }
        }

        private void LoadPredictor()
        {
            lock (Mutex)
            {
                this.Dispatcher.Invoke((Action)(() =>
                {
                    this.statusBarStatusLabel.Text = "Opening...";
                }
                ), null);

                string fileName = string.Empty;

                this.Dispatcher.Invoke((Action)(() =>
                {
                    OpenFileDialog ofd = new OpenFileDialog();
                    {
                        ofd.Filter = "Hopfiled Predictor File (*.hpf)|*.hpf";
                        if (ofd.ShowDialog() != true) return;
                        fileName = ofd.FileName;
                    }
                }
                ), null);

                if (true == string.IsNullOrEmpty(fileName)) return;

                DataSet ds = new DataSet("Predictor");
                ds.Locale = new CultureInfo("en-US");

                try
                {
                    ds.Tables.Add(this.dataTablePredictor);
                    this.dataTablePredictor.Rows.Clear();
                    ds.ReadXml(fileName);
                }
                finally
                {
                    ds.Tables.Remove(this.dataTablePredictor);
                }

                this.Dispatcher.Invoke((Action)(() =>
                {
                    this.dataGridPredictor.ItemsSource = this.dataTablePredictor.DefaultView;
                    this.RefreshTabPages();
                    this.statusBarStatusLabel.Text = "Ready.";
                }
                ), null);
            }
        }
        #endregion
    }
}
